home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / mgraph / xgraph.cc < prev    next >
C/C++ Source or Header  |  1994-03-22  |  15KB  |  506 lines

  1. /* Copyright 1994 Ralph Gonzalez */
  2.  
  3. /*
  4. *    FILE:        xgraph.c
  5. *    AUTHOR:        R. Gonzalez, partially adapted from code by Don Snow
  6. *    CREATED:    April 16, 1993
  7. *
  8. *    xgraph.c contains several X Window routines to supplement
  9. *    a typical stdio-type application with a graphics window.
  10. *    Your program must #include "xgraph.h" and must call
  11. *    init_graphics() first.
  12. *    
  13. *    The device coordinate system (usually given in pixels) is norm-
  14. *    alized to -1 to 1 horizontally by default. The vertical limits
  15. *    depend on the screen's aspect ratio. This is used for placing and
  16. *    sizing the graphics window.
  17. *
  18. *    The window's own view coordinate system (which is used for
  19. *    drawing) is also normalized, by default to -1 to 1 horizontally
  20. *    and vertically. 
  21. */
  22.  
  23. # include    "xgraph.h"
  24. # include    <stdio.h>
  25. # include    <stdlib.h>
  26. # include    <string.h>
  27. # include    <X11/Xlib.h>
  28. # include    <X11/Xutil.h>
  29.  
  30. /*---------------------------------------------------------------------*/
  31. /*    The following #define's may be adjusted to your taste: */
  32. /*---------------------------------------------------------------------*/
  33. # define    TITLE                "Graphics"
  34. # define    MOVABLE                    /* delete this for immobile window */
  35. # define    BACKGROUND            BLACK
  36. # define    GRAPH_WIDTH            1.    /* Window sizing & placement in */
  37. # define    GRAPH_HEIGHT        1.    /* normalized screen coords:    */
  38. # define    GRAPH_CENTER_X        0.    /* center is (0,0), width is 2. */
  39. # define    GRAPH_CENTER_Y        0.
  40. /*---------------------------------------------------------------------*/
  41. /*    You can change these too if you want: */
  42. /*---------------------------------------------------------------------*/
  43. # define    NORM_WIDTH            2.    /* define normalized screen coords */
  44.                                     /* height depends on aspect ratio */
  45. # define    NORM_CENTER_X        0.
  46. # define    NORM_CENTER_Y        0.
  47. # define    VIEW_WIDTH            2.    /* define normalized window coords */
  48. # define    VIEW_HEIGHT            2.
  49. # define    VIEW_CENTER_X        0.
  50. # define    VIEW_CENTER_Y        0.
  51. /*---------------------------------------------------------------------*/
  52.  
  53. # define    BORDER_WIDTH        0
  54. /* X has alot more colors, but we're only using 8 */
  55. # define    maxPixels             8
  56.  
  57. /* globals for graphics coordinate system */
  58. static double        gGraphWidth,
  59.                     gGraphHeight,
  60.                     gGraphX,
  61.                     gGraphY,
  62.                     gViewWidth,
  63.                     gViewHeight,
  64.                     gViewX,
  65.                     gViewY;
  66.  
  67. static int            transform_x(double),
  68.                     transform_y(double);
  69.  
  70. /* globals for XWindow implementation */
  71. static Display            *gDisplay;
  72. static int                 gDepth;
  73. static int                gScreen;
  74. static unsigned long    gBlackPixel;
  75. static unsigned long    gWhitePixel;
  76. static unsigned long    gPixels[maxPixels];
  77. static char                *gColorNames[maxPixels] = 
  78.     {"Black","White","Red","Yellow","Green","Blue","Cyan","Magenta"};
  79. static Colormap            gColormap;
  80. static GC                gGC;
  81. static Window            gGraphWindow;
  82. static int                gPenX=0,gPenY=0;
  83.  
  84. /* globals for screen coordinate system (for window sizing) */
  85. static double    gDeviceWidth,
  86.                 gDeviceHeight,
  87.                 gDeviceCenterX,
  88.                 gDeviceCenterY,
  89.                 gNormalDeviceWidth,
  90.                 gNormalDeviceHeight,
  91.                 gNormalDeviceCenterX,
  92.                 gNormalDeviceCenterY;
  93.  
  94. /************************************************************************
  95. *    You must call init_graphics() at the beginning of main().
  96. ************************************************************************/
  97. void    init_graphics(void)
  98. {
  99.     Window            rootDummy;
  100.     XColor            theRGBColor, theHardwareColor;
  101.     XGCValues        theGCValues;
  102.     int                   theStatus;
  103.     XSizeHints        theSizeHints;
  104.     XWMHints        theWMHints;
  105.     XSetWindowAttributes    theWindowAttributes;
  106.     unsigned long    theWindowAttributesMask;
  107.     int                i;
  108.     double            ratio,
  109.                     temp;
  110.     int                left,
  111.                     top;
  112.     unsigned int    width,
  113.                     height,
  114.                     borderDummy,
  115.                     depthDummy;
  116.  
  117.     gDisplay = XOpenDisplay(NULL);
  118.     if (gDisplay == NULL)
  119.     {
  120.         printf("SCREEN ERROR: Cannot connect to the X Server.\n");
  121.         return;
  122.     }
  123.     gScreen = DefaultScreen(gDisplay);
  124.     gDepth = DefaultDepth(gDisplay,gScreen);
  125.     gBlackPixel = BlackPixel(gDisplay,gScreen);
  126.     gWhitePixel = WhitePixel(gDisplay,gScreen);
  127.     gColormap = DefaultColormap(gDisplay,gScreen);
  128.  
  129.     /* initalize the color array */
  130.  
  131.     if (gDepth > 1)
  132.     {
  133.         for (i=0; i < maxPixels; i++)
  134.         {
  135.             theStatus = XLookupColor(gDisplay,gColormap,gColorNames[i],
  136.                    &theRGBColor,&theHardwareColor);
  137.             if (theStatus != 0)
  138.             {
  139.                 theStatus = XAllocColor(gDisplay,gColormap,
  140.                       &theHardwareColor);
  141.                 if (theStatus != 0)
  142.                 {
  143.                     gPixels[i] = theHardwareColor.pixel;
  144.                 }
  145.                 else
  146.                 {
  147.                     gPixels[i] = gBlackPixel;
  148.                 }
  149.             }
  150.         }
  151.     }
  152.     else /* initalize color aray for Black and White display */
  153.     {
  154.         for(i=0; i<maxPixels; i++)
  155.         {
  156.             if (strcmp("White", gColorNames[i]) == 0)
  157.             {
  158.                 gPixels[i] = gWhitePixel;
  159.             }
  160.             else
  161.             {
  162.                 gPixels[i] = gBlackPixel;
  163.             }
  164.         }
  165.     }
  166.  
  167.     /* Create the GC */
  168.  
  169.     gGC = XCreateGC(gDisplay,RootWindow(gDisplay,gScreen),
  170.             (unsigned long) 0,&theGCValues);
  171.   
  172.     if (gGC == 0)
  173.     {
  174.         printf("SCREEN ERROR: Couldn't create a new GC.\n");
  175.         return;
  176.     }
  177.  
  178.     /* calculate device and normalized screen coord systems */
  179.  
  180.     gDeviceWidth = DisplayWidth(gDisplay,gScreen);
  181.     gDeviceHeight = -DisplayHeight(gDisplay,gScreen);
  182.     gDeviceCenterX = gDeviceWidth/2;
  183.     gDeviceCenterY = -gDeviceHeight/2;
  184.     
  185.     ratio = get_screen_aspect_ratio();
  186.     
  187.     gNormalDeviceWidth = NORM_WIDTH;
  188.     gNormalDeviceHeight = gNormalDeviceWidth/ratio;
  189.     gNormalDeviceCenterX = NORM_CENTER_X;
  190.     gNormalDeviceCenterY = NORM_CENTER_Y;
  191.  
  192.     /* convert window location from normalized to device coords */
  193.     
  194.     temp = GRAPH_CENTER_X-GRAPH_WIDTH/2.;
  195.     ratio = gDeviceWidth/gNormalDeviceWidth;
  196.     left = (int) (gDeviceCenterX + (temp-gNormalDeviceCenterX) * ratio);
  197.     width = (int) (ratio*GRAPH_WIDTH);
  198.     
  199.     temp = GRAPH_CENTER_Y+GRAPH_HEIGHT/2.;
  200.     ratio = gDeviceHeight/gNormalDeviceHeight;
  201.     top = (int) (gDeviceCenterY + (temp-gNormalDeviceCenterY) * ratio);
  202.     height = (int) (-ratio*GRAPH_HEIGHT);
  203.     
  204.     /* create graphics window */
  205.  
  206.     gGraphWindow =
  207.     XCreateSimpleWindow(gDisplay,RootWindow(gDisplay,gScreen),
  208.                 left,top,width,height,BORDER_WIDTH,
  209.                 gPixels[WHITE],gPixels[BLACK]);
  210.     XStoreName(gDisplay,gGraphWindow,TITLE);
  211.     
  212. # ifndef MOVABLE
  213.     /* override_redirect: tells the window manager running on the */
  214.     /* diplay to leave this window alone (ie no title bar, no */
  215.     /* resizing, ect. */
  216.  
  217.     theWindowAttributes.override_redirect = TRUE;
  218.     theWindowAttributesMask = CWOverrideRedirect;
  219.     XChangeWindowAttributes(gDisplay,gGraphWindow,
  220.                   theWindowAttributesMask,&theWindowAttributes);
  221. # endif
  222.  
  223.     /* NormalState: ie not iconified, shrunk, etc */
  224.  
  225.     theWMHints.initial_state = NormalState;
  226.     theWMHints.flags = StateHint;
  227.     XSetWMHints(gDisplay,gGraphWindow,&theWMHints);
  228.  
  229.     /* USPostion,USSize: Specifies that the user supplied the */
  230.     /* position and size of the window (possibly as command line */
  231.     /* arguments). This is actually a lie, as we don't want the user */
  232.     /* selecting the position and size of the window, and the WM */
  233.     /* would let them do just that without these specifications. */
  234.  
  235.     theSizeHints.flags = USPosition | USSize;
  236.     XSetNormalHints(gDisplay,gGraphWindow,&theSizeHints);
  237.  
  238.     XMapWindow(gDisplay,gGraphWindow);
  239.     XFlush(gDisplay);
  240.       
  241.     /* Set up for input from this window */
  242.       
  243.     XSelectInput(gDisplay,gGraphWindow,ButtonPressMask);
  244.  
  245.     /* find window coordinates, set normalized window coords */
  246.     
  247.     XGetGeometry(gDisplay,gGraphWindow,&rootDummy,&left,&top,
  248.         &width,&height,&borderDummy,&depthDummy);
  249.     gGraphX = width/2;
  250.     gGraphY = height/2;
  251.     gGraphWidth = width;
  252.     gGraphHeight = -((int) height);    /* can't negate unsigned int */
  253.     
  254.     set_graphics_coords(VIEW_WIDTH,VIEW_HEIGHT,VIEW_CENTER_X,VIEW_CENTER_Y);
  255.     pen_color(WHITE);
  256.     background_color(BLACK);
  257.     erase_graphics();
  258.     
  259. # ifdef MOVABLE
  260.     sleep(1);    /* wait for window manager to kick in */
  261. # endif
  262. }
  263.  
  264. /************************************************************************
  265. *    Return device aspect ratio: width/height
  266. ************************************************************************/
  267. double    get_screen_aspect_ratio(void)
  268. {
  269.     double    ratio;
  270.     
  271.     ratio = gDeviceWidth/gDeviceHeight;
  272.     if (ratio < 0.)
  273.         ratio = -ratio;
  274.         
  275.     return ratio;
  276. }
  277.  
  278. /************************************************************************
  279. *    Define view coordinate system for draw_line(), draw_circle(), etc.
  280. ************************************************************************/
  281. void    set_graphics_coords(double width,double height,double x,double y)
  282. {
  283.     gViewWidth = width;
  284.     gViewHeight = height;
  285.     gViewX = x;
  286.     gViewY = y;
  287. }
  288.  
  289. /************************************************************************
  290. *    Transform view coordinate x to window coordinates
  291. ************************************************************************/
  292. static int    transform_x(double x)
  293. {
  294.     return (int) (gGraphX+(x-gViewX)*gGraphWidth/gViewWidth);
  295. }
  296.  
  297. /************************************************************************
  298. *    Transform view coordinate y to window coordinates
  299. ************************************************************************/
  300. static int    transform_y(double y)
  301. {
  302.     return (int) (gGraphY+(y-gViewY)*gGraphHeight/gViewHeight);
  303. }
  304.  
  305. /************************************************************************
  306. *    pen_color() sets the current drawing color. 
  307. ************************************************************************/
  308. void    pen_color(color x)
  309. {    
  310.     XSetForeground(gDisplay,gGC,gPixels[x]);
  311. }
  312.  
  313. /************************************************************************
  314. *    background_color() sets the background drawing color.
  315. ************************************************************************/
  316. void    background_color(color x)
  317. {    
  318.     XSetWindowBackground(gDisplay,gGraphWindow,gPixels[x]); 
  319. }
  320.  
  321. /************************************************************************
  322. *    bring graphics window to front. 
  323. ************************************************************************/
  324. void    graphics_to_front(void)
  325. {
  326.     XRaiseWindow(gDisplay,gGraphWindow); 
  327. }
  328.  
  329. /************************************************************************
  330. *    erase_graphics() makes the graphics window the background color. 
  331. ************************************************************************/
  332. void    erase_graphics(void)
  333. {
  334.     XClearWindow(gDisplay,gGraphWindow);
  335.     XFlush(gDisplay);
  336. }
  337.  
  338. /************************************************************************
  339. *    draw_line() is used to draw lines using view coordinates.
  340. ************************************************************************/
  341. void        draw_line(double x1,double y1,double x2,double y2)
  342. {
  343.     int        window_x1,
  344.             window_y1,
  345.             window_x2,
  346.             window_y2;
  347.  
  348.     window_x1 = transform_x(x1);
  349.     window_y1 = transform_y(y1);
  350.     window_x2 = transform_x(x2);
  351.     window_y2 = transform_y(y2);
  352.     
  353.     XDrawLine(gDisplay,gGraphWindow,gGC,window_x1,window_y1,
  354.         window_x2,window_y2);
  355.     XFlush(gDisplay);
  356.     
  357.     gPenX = window_x2;
  358.     gPenY = window_y2;
  359. }
  360.  
  361. /************************************************************************
  362. *    Move present pen position to new position using view
  363. *    coordinates.  Nothing is drawn.
  364. ************************************************************************/
  365. void    move_to(double x,double y)
  366. {
  367.     gPenX = transform_x(x);
  368.     gPenY = transform_y(y);
  369. }
  370.  
  371. /************************************************************************
  372. *    Draw from present pen position to new position using view
  373. *    coordinates.
  374. ************************************************************************/
  375. void    draw_to(double x,double y)
  376. {
  377.     int        window_x,
  378.             window_y;
  379.             
  380.     window_x = transform_x(x);
  381.     window_y = transform_y(y);
  382.     
  383.     XDrawLine(gDisplay,gGraphWindow,gGC,gPenX,gPenY,window_y,window_y);
  384.     XFlush(gDisplay);
  385.     gPenX =  window_x;
  386.     gPenY =  window_y;
  387. }
  388.  
  389. /************************************************************************
  390. *    draw_circle() draws a circle using view coordinates.
  391. ************************************************************************/
  392. void draw_circle(double center_x,double center_y,double r)
  393. {
  394.     int                window_x,
  395.                     window_y,
  396.                     window_r,
  397.                     start_angle = 0,
  398.                     path_angle = 360*64;    /* in 1/64 of degrees */
  399.     unsigned int    width,
  400.                     height;
  401.             
  402.     window_x = transform_x(center_x);
  403.     window_y = transform_y(center_y);
  404.     window_r = (int) (r*gGraphWidth/gViewWidth);
  405.     window_x = window_x - window_r;
  406.     window_y = window_y - window_r;
  407.     width = window_r*2;
  408.     height = width;    
  409.     
  410.     XDrawArc(gDisplay,gGraphWindow,gGC,window_x,window_y,width,height,
  411.         start_angle,path_angle);
  412.     XFlush(gDisplay);
  413.     gPenX =  window_x;
  414.     gPenY =  window_y;
  415. }
  416.  
  417. /************************************************************************
  418. *    fill_circle() draws a circle using view coordinates.  The circle
  419. *    is filled with the present pen color
  420. ************************************************************************/
  421. void fill_circle(double center_x,double center_y,double r)
  422. {
  423.     int                window_x,
  424.                     window_y,
  425.                     window_r,
  426.                     start_angle = 0,
  427.                     path_angle = 360*64;    /* in 1/64 of degrees */
  428.     unsigned int    width,
  429.                     height;
  430.             
  431.     window_x = transform_x(center_x);
  432.     window_y = transform_y(center_y);
  433.     window_r = (int) (r*gGraphWidth/gViewWidth);
  434.     window_x = window_x - window_r;
  435.     window_y = window_y - window_r;
  436.     width = window_r*2;
  437.     height = width;    
  438.     
  439.     XFillArc(gDisplay,gGraphWindow,gGC,window_x,window_y,width,height,
  440.         start_angle,path_angle);
  441.     XFlush(gDisplay);
  442.     gPenX =  window_x;
  443.     gPenY =  window_y;
  444. }
  445.  
  446. /************************************************************************
  447. *    mouse_button_is_down() checks whether the mouse button is down,
  448. *    returns TRUE or FALSE.
  449. ************************************************************************/
  450. boolean        mouse_button_is_down(void)
  451. {
  452.     XEvent    theEventDummy;
  453.     
  454.     if (XPending(gDisplay))
  455.     {
  456.         XNextEvent(gDisplay,&theEventDummy);
  457.         return TRUE;
  458.     }
  459.     else
  460.         return FALSE;
  461. }
  462.  
  463. /************************************************************************
  464. *    wait until button is pressed
  465. ************************************************************************/
  466. void    wait(void)
  467. {
  468.     XEvent    theEventDummy;
  469.   
  470.     XNextEvent(gDisplay,&theEventDummy);
  471. }
  472.  
  473. /************************************************************************
  474. *    get_mouse_location returns the mouse coordinates in terms of the 
  475. *    view coordinate system. It uses the inverse transformation of that
  476. *    in transform_x() and transform_y().
  477. ************************************************************************/
  478. void    get_mouse_location(double *x_ptr,double *y_ptr)
  479. {
  480.     static Window    dummy_root,dummy_child; /* static so we don't have
  481.                                             to allocate them repeatedly */
  482.     boolean            in_screen;
  483.     int                root_x,
  484.                     root_y,
  485.                     x,
  486.                     y;
  487.     unsigned int    button_state;
  488.     
  489.     in_screen = XQueryPointer(gDisplay,gGraphWindow,&dummy_root,
  490.         &dummy_child,&root_x,&root_y,&x,&y,&button_state);
  491.     
  492.     if (in_screen)
  493.     {
  494.         *x_ptr = gViewX+(x-gGraphX)*gViewWidth/gGraphWidth;
  495.         *y_ptr = gViewY+(y-gGraphY)*gViewHeight/gGraphHeight;
  496.     }
  497.     else
  498.     {
  499.         *x_ptr = 0.;
  500.         *y_ptr = 0.;
  501.     }
  502.  
  503.     return;
  504. }
  505.  
  506.